#include "ocean.h"
#include "layersmanager.h"
#include "skydomeactor.h"
#include <osg\LightSource>
#include "skydome.h"

namespace VrDataCore
{
	Ocean::Ocean(CoreBase::ActorBase* owneractor,const QString& name):CoreBase::Transformable(owneractor,name),_size(129,129)
		,_inScene(false),_root(NULL)
	{
		_windDirection = osg::Vec2f(1.0f,1.0f);
		_windSpeed = 12.0f;
		_depth = 10000.f;
		_reflectionDamping = 0.35f;
		_waveScaleBase = 1.0e-8;
		_isChoppy = true;
		_choppyFactor = -2.5f;
		_crestFoamHeight = 2.2f;
		_foamHeightScale = 0;
		_waveScale = 100;
	}

	Ocean::~Ocean()
	{}

	void Ocean::setEnvironmentParameter(osg::TextureCubeMap* texture,const osg::Vec4& lightcolor,const osg::Vec4& fogcolors,
		const osg::Vec4& waterfogcolors,const osg::Vec4& underwaterdiffuse,const osg::Vec3& underwaterattenuations
		,const osg::Vec3& sunpositions,const osg::Vec4& sundiffuse)
	{
		if(_inScene)
		{
			_oceanSurface->setEnvironmentMap(texture);
			_oceanSurface->setLightColor(lightcolor);
			_oceanScene->setAboveWaterFog(0.00012f,fogcolors);
			//_oceanScene->setAboveWaterFog(0.0f,fogcolors);
			_oceanScene->setUnderwaterFog(0.002f,waterfogcolors);
			_oceanScene->setUnderwaterDiffuse(underwaterdiffuse);
			_oceanScene->setUnderwaterAttenuation(underwaterattenuations);
			osg::Vec3f sunDir = -sunpositions;
			sunDir.normalize();
			_oceanScene->setSunDirection( sunDir );
			_light->setPosition(osg::Vec4f(sunpositions,1.f));
			_light->setDiffuse(sundiffuse);
		}
		else
		{
			_parameter._tex = texture;
			_parameter._lightColors = lightcolor;
			_parameter._fogColors = fogcolors;
			_parameter._waterFogColors = waterfogcolors;
			_parameter._underwaterDiffuse = underwaterdiffuse;
			_parameter._underwaterAttenuations = underwaterattenuations;
			_parameter._sunPositions = sunpositions;
			_parameter._sunDiffuse = sundiffuse;
		}
	}

	void Ocean::setSize(const osg::Vec2& size)
	{
		_size = size;
		if(_inScene)
		{
			getOSGNode()->asGroup()->removeChild(_root);
			_oceanScene = NULL;
			_root = NULL;
			_oceanSurface = NULL;
			build( _parameter._tex,_parameter._lightColors,_parameter._fogColors,
				_parameter._waterFogColors,_parameter._underwaterDiffuse,
				_parameter._underwaterAttenuations,_parameter._sunPositions,_parameter._sunDiffuse);
		}
	}

	void Ocean::addedToScene(CoreBase::Scene* scene)
	{
		if(scene!=NULL)
		{
			_inScene = true;
			if(!_root.valid())
				build( _parameter._tex,_parameter._lightColors,_parameter._fogColors,
					_parameter._waterFogColors,_parameter._underwaterDiffuse,
					_parameter._underwaterAttenuations,_parameter._sunPositions,_parameter._sunDiffuse);
		}
		else
			_inScene = false;
		CoreBase::Transformable::addedToScene(scene);	
	}

	void Ocean::setFoamHeight(int h)
	{
		if(h==_foamHeightScale)
			return;
		_foamHeightScale = h;
		if(_oceanSurface.valid())
		{
			float scale = h/100.0f;
			_oceanSurface->setFoamBottomHeight(2.2f*(1-scale));
			_oceanSurface->setFoamTopHeight(3.0f*(1-scale));
		}
	}

	int Ocean::getFoamHeight()
	{
		return _foamHeightScale;
	}

	void Ocean::setWaveScale(int s)
	{
		if(s==_waveScale)
			return;
		_waveScale = s;
		if(_oceanSurface.valid())
		{
			float scale = _waveScale/100.0f;
			_oceanSurface->setWaveScaleFactor(_waveScaleBase*scale);
		}
	}

	int Ocean::getWaveScale()
	{
		return _waveScale;
	}

	void Ocean::build( osg::TextureCubeMap* texture,const osg::Vec4& lightcolor,const osg::Vec4& fogcolors,
		const osg::Vec4& waterfogcolors,const osg::Vec4& underwaterdiffuse,const osg::Vec3& underwaterattenuations
		,const osg::Vec3& sunpositions,const osg::Vec4& sundiffuse)
	{
		_root = new osg::Group;
		float scale = _waveScale/100.0f;
		_oceanSurface = new osgOcean::FFTOceanSurface( 64, 256, _size.x(),_size.y(),//64,256,17
			_windDirection, _windSpeed, _depth, _reflectionDamping, _waveScaleBase*scale, _isChoppy, _choppyFactor, 10.f, 200 );//256
		_oceanSurface->setEnvironmentMap(texture);
		scale = _foamHeightScale/100.0f;
		_oceanSurface->setFoamBottomHeight(2.2f*(1-scale));
		_oceanSurface->setFoamTopHeight(3.0f*(1-scale));
		_oceanSurface->enableCrestFoam( true );
		_oceanSurface->setLightColor(lightcolor);
		// Make the ocean surface track with the main camera position, giving the illusion
		// of an endless ocean surface.
		_oceanSurface->enableEndlessOcean(false);
		// Set up ocean scene, add surface
		osg::Vec3f sunDir = -sunpositions;
		sunDir.normalize();
		_oceanScene = new osgOcean::OceanScene( _oceanSurface.get() );
		_oceanScene->setLightID(0);
		_oceanScene->enableReflections(true);
		_oceanScene->enableRefractions(true);
		// Set the size of _oceanCylinder which follows the camera underwater. 
		// This cylinder prevents the clear from being visible past the far plane 
		// instead it will be the fog color.
		// The size of the cylinder should be changed according the size of the ocean surface.
		//_oceanScene->setCylinderSize(100000,100000.f);
		_oceanScene->setAboveWaterFog(0.00012f,fogcolors);
		//_oceanScene->setAboveWaterFog(0.0f,fogcolors);
		_oceanScene->setUnderwaterFog(0.002f,waterfogcolors);
		_oceanScene->setUnderwaterDiffuse(underwaterdiffuse);
		_oceanScene->setUnderwaterAttenuation(underwaterattenuations);
		_oceanScene->setSunDirection( sunDir );
		_oceanScene->enableGodRays(true);
		_oceanScene->enableSilt(true);
		_oceanScene->enableUnderwaterDOF(true);
		_oceanScene->enableDistortion(true);
		_oceanScene->enableGlare(false);
		_oceanScene->setGlareAttenuation(0.8f);
		// Create and add fake texture for use with nodes without any texture
		// since the OceanScene default scene shader assumes that texture unit 
		// 0 is used as a base texture map.
		osg::Image * image = new osg::Image;
		image->allocateImage( 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE );
		*(osg::Vec4ub*)image->data() = osg::Vec4ub( 0xFF, 0xFF, 0xFF, 0xFF );

		osg::Texture2D* fakeTex = new osg::Texture2D( image );
		fakeTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);
		fakeTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);
		fakeTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
		fakeTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);

		osg::StateSet* stateset = _oceanScene->getOrCreateStateSet();
		stateset->setTextureAttribute(0,fakeTex,osg::StateAttribute::ON);
		stateset->setTextureMode(0,GL_TEXTURE_1D,osg::StateAttribute::OFF);
		stateset->setTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::ON);
		stateset->setTextureMode(0,GL_TEXTURE_3D,osg::StateAttribute::OFF);
		osg::LightSource* lightSource = new osg::LightSource;
		lightSource->setLocalStateSetModes();
		_light = lightSource->getLight();
		_light->setLightNum(6);
		_light->setAmbient( osg::Vec4d(0.3f, 0.3f, 0.3f, 1.0f ));
		_light->setDiffuse( sundiffuse );
		_light->setSpecular(osg::Vec4d( 0.1f, 0.1f, 0.1f, 1.0f ) );
		_light->setPosition( osg::Vec4f(sunpositions, 1.f) ); // point light
		_root->addChild( lightSource );
		_root->addChild( _oceanScene.get());
		getOSGNode()->asGroup()->addChild(_root);
	}

	void Ocean::setEnvironmentColor(const osg::Vec4& color)
	{
		_oceanSurface->setEnvironmentColor(color);
	}

	bool Ocean::addChild(CoreBase::Drawable* child)
	{
		if(dynamic_cast<SkyDome*>(child)!=NULL)
		{
			_oceanScene->addChild(child->getOSGNode());
			child->getOSGNode()->setNodeMask( _oceanScene->getReflectedSceneMask() | _oceanScene->getNormalSceneMask() );
		}
		return true;
	}

	void Ocean::getOceanSurfaceHeight(osg::Vec3& pos)
	{
		pos.z() = _oceanSurface->getSurfaceHeightAt(pos.x(),pos.y());
	}
}